#![allow(unreachable_patterns)]

use std::ffi::{CString, CStr};
use libc::c_char;
use std::mem::transmute;

use crate::sys;
use crate::sys::SDL_Scancode;

#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Scancode {
    A                  = SDL_Scancode::SDL_SCANCODE_A as i32,
    B                  = SDL_Scancode::SDL_SCANCODE_B as i32,
    C                  = SDL_Scancode::SDL_SCANCODE_C as i32,
    D                  = SDL_Scancode::SDL_SCANCODE_D as i32,
    E                  = SDL_Scancode::SDL_SCANCODE_E as i32,
    F                  = SDL_Scancode::SDL_SCANCODE_F as i32,
    G                  = SDL_Scancode::SDL_SCANCODE_G as i32,
    H                  = SDL_Scancode::SDL_SCANCODE_H as i32,
    I                  = SDL_Scancode::SDL_SCANCODE_I as i32,
    J                  = SDL_Scancode::SDL_SCANCODE_J as i32,
    K                  = SDL_Scancode::SDL_SCANCODE_K as i32,
    L                  = SDL_Scancode::SDL_SCANCODE_L as i32,
    M                  = SDL_Scancode::SDL_SCANCODE_M as i32,
    N                  = SDL_Scancode::SDL_SCANCODE_N as i32,
    O                  = SDL_Scancode::SDL_SCANCODE_O as i32,
    P                  = SDL_Scancode::SDL_SCANCODE_P as i32,
    Q                  = SDL_Scancode::SDL_SCANCODE_Q as i32,
    R                  = SDL_Scancode::SDL_SCANCODE_R as i32,
    S                  = SDL_Scancode::SDL_SCANCODE_S as i32,
    T                  = SDL_Scancode::SDL_SCANCODE_T as i32,
    U                  = SDL_Scancode::SDL_SCANCODE_U as i32,
    V                  = SDL_Scancode::SDL_SCANCODE_V as i32,
    W                  = SDL_Scancode::SDL_SCANCODE_W as i32,
    X                  = SDL_Scancode::SDL_SCANCODE_X as i32,
    Y                  = SDL_Scancode::SDL_SCANCODE_Y as i32,
    Z                  = SDL_Scancode::SDL_SCANCODE_Z as i32,
    Num1               = SDL_Scancode::SDL_SCANCODE_1 as i32,
    Num2               = SDL_Scancode::SDL_SCANCODE_2 as i32,
    Num3               = SDL_Scancode::SDL_SCANCODE_3 as i32,
    Num4               = SDL_Scancode::SDL_SCANCODE_4 as i32,
    Num5               = SDL_Scancode::SDL_SCANCODE_5 as i32,
    Num6               = SDL_Scancode::SDL_SCANCODE_6 as i32,
    Num7               = SDL_Scancode::SDL_SCANCODE_7 as i32,
    Num8               = SDL_Scancode::SDL_SCANCODE_8 as i32,
    Num9               = SDL_Scancode::SDL_SCANCODE_9 as i32,
    Num0               = SDL_Scancode::SDL_SCANCODE_0 as i32,
    Return             = SDL_Scancode::SDL_SCANCODE_RETURN as i32,
    Escape             = SDL_Scancode::SDL_SCANCODE_ESCAPE as i32,
    Backspace          = SDL_Scancode::SDL_SCANCODE_BACKSPACE as i32,
    Tab                = SDL_Scancode::SDL_SCANCODE_TAB as i32,
    Space              = SDL_Scancode::SDL_SCANCODE_SPACE as i32,
    Minus              = SDL_Scancode::SDL_SCANCODE_MINUS as i32,
    Equals             = SDL_Scancode::SDL_SCANCODE_EQUALS as i32,
    LeftBracket        = SDL_Scancode::SDL_SCANCODE_LEFTBRACKET as i32,
    RightBracket       = SDL_Scancode::SDL_SCANCODE_RIGHTBRACKET as i32,
    Backslash          = SDL_Scancode::SDL_SCANCODE_BACKSLASH as i32,
    NonUsHash          = SDL_Scancode::SDL_SCANCODE_NONUSHASH as i32,
    Semicolon          = SDL_Scancode::SDL_SCANCODE_SEMICOLON as i32,
    Apostrophe         = SDL_Scancode::SDL_SCANCODE_APOSTROPHE as i32,
    Grave              = SDL_Scancode::SDL_SCANCODE_GRAVE as i32,
    Comma              = SDL_Scancode::SDL_SCANCODE_COMMA as i32,
    Period             = SDL_Scancode::SDL_SCANCODE_PERIOD as i32,
    Slash              = SDL_Scancode::SDL_SCANCODE_SLASH as i32,
    CapsLock           = SDL_Scancode::SDL_SCANCODE_CAPSLOCK as i32,
    F1                 = SDL_Scancode::SDL_SCANCODE_F1 as i32,
    F2                 = SDL_Scancode::SDL_SCANCODE_F2 as i32,
    F3                 = SDL_Scancode::SDL_SCANCODE_F3 as i32,
    F4                 = SDL_Scancode::SDL_SCANCODE_F4 as i32,
    F5                 = SDL_Scancode::SDL_SCANCODE_F5 as i32,
    F6                 = SDL_Scancode::SDL_SCANCODE_F6 as i32,
    F7                 = SDL_Scancode::SDL_SCANCODE_F7 as i32,
    F8                 = SDL_Scancode::SDL_SCANCODE_F8 as i32,
    F9                 = SDL_Scancode::SDL_SCANCODE_F9 as i32,
    F10                = SDL_Scancode::SDL_SCANCODE_F10 as i32,
    F11                = SDL_Scancode::SDL_SCANCODE_F11 as i32,
    F12                = SDL_Scancode::SDL_SCANCODE_F12 as i32,
    PrintScreen        = SDL_Scancode::SDL_SCANCODE_PRINTSCREEN as i32,
    ScrollLock         = SDL_Scancode::SDL_SCANCODE_SCROLLLOCK as i32,
    Pause              = SDL_Scancode::SDL_SCANCODE_PAUSE as i32,
    Insert             = SDL_Scancode::SDL_SCANCODE_INSERT as i32,
    Home               = SDL_Scancode::SDL_SCANCODE_HOME as i32,
    PageUp             = SDL_Scancode::SDL_SCANCODE_PAGEUP as i32,
    Delete             = SDL_Scancode::SDL_SCANCODE_DELETE as i32,
    End                = SDL_Scancode::SDL_SCANCODE_END as i32,
    PageDown           = SDL_Scancode::SDL_SCANCODE_PAGEDOWN as i32,
    Right              = SDL_Scancode::SDL_SCANCODE_RIGHT as i32,
    Left               = SDL_Scancode::SDL_SCANCODE_LEFT as i32,
    Down               = SDL_Scancode::SDL_SCANCODE_DOWN as i32,
    Up                 = SDL_Scancode::SDL_SCANCODE_UP as i32,
    NumLockClear       = SDL_Scancode::SDL_SCANCODE_NUMLOCKCLEAR as i32,
    KpDivide           = SDL_Scancode::SDL_SCANCODE_KP_DIVIDE as i32,
    KpMultiply         = SDL_Scancode::SDL_SCANCODE_KP_MULTIPLY as i32,
    KpMinus            = SDL_Scancode::SDL_SCANCODE_KP_MINUS as i32,
    KpPlus             = SDL_Scancode::SDL_SCANCODE_KP_PLUS as i32,
    KpEnter            = SDL_Scancode::SDL_SCANCODE_KP_ENTER as i32,
    Kp1                = SDL_Scancode::SDL_SCANCODE_KP_1 as i32,
    Kp2                = SDL_Scancode::SDL_SCANCODE_KP_2 as i32,
    Kp3                = SDL_Scancode::SDL_SCANCODE_KP_3 as i32,
    Kp4                = SDL_Scancode::SDL_SCANCODE_KP_4 as i32,
    Kp5                = SDL_Scancode::SDL_SCANCODE_KP_5 as i32,
    Kp6                = SDL_Scancode::SDL_SCANCODE_KP_6 as i32,
    Kp7                = SDL_Scancode::SDL_SCANCODE_KP_7 as i32,
    Kp8                = SDL_Scancode::SDL_SCANCODE_KP_8 as i32,
    Kp9                = SDL_Scancode::SDL_SCANCODE_KP_9 as i32,
    Kp0                = SDL_Scancode::SDL_SCANCODE_KP_0 as i32,
    KpPeriod           = SDL_Scancode::SDL_SCANCODE_KP_PERIOD as i32,
    NonUsBackslash     = SDL_Scancode::SDL_SCANCODE_NONUSBACKSLASH as i32,
    Application        = SDL_Scancode::SDL_SCANCODE_APPLICATION as i32,
    Power              = SDL_Scancode::SDL_SCANCODE_POWER as i32,
    KpEquals           = SDL_Scancode::SDL_SCANCODE_KP_EQUALS as i32,
    F13                = SDL_Scancode::SDL_SCANCODE_F13 as i32,
    F14                = SDL_Scancode::SDL_SCANCODE_F14 as i32,
    F15                = SDL_Scancode::SDL_SCANCODE_F15 as i32,
    F16                = SDL_Scancode::SDL_SCANCODE_F16 as i32,
    F17                = SDL_Scancode::SDL_SCANCODE_F17 as i32,
    F18                = SDL_Scancode::SDL_SCANCODE_F18 as i32,
    F19                = SDL_Scancode::SDL_SCANCODE_F19 as i32,
    F20                = SDL_Scancode::SDL_SCANCODE_F20 as i32,
    F21                = SDL_Scancode::SDL_SCANCODE_F21 as i32,
    F22                = SDL_Scancode::SDL_SCANCODE_F22 as i32,
    F23                = SDL_Scancode::SDL_SCANCODE_F23 as i32,
    F24                = SDL_Scancode::SDL_SCANCODE_F24 as i32,
    Execute            = SDL_Scancode::SDL_SCANCODE_EXECUTE as i32,
    Help               = SDL_Scancode::SDL_SCANCODE_HELP as i32,
    Menu               = SDL_Scancode::SDL_SCANCODE_MENU as i32,
    Select             = SDL_Scancode::SDL_SCANCODE_SELECT as i32,
    Stop               = SDL_Scancode::SDL_SCANCODE_STOP as i32,
    Again              = SDL_Scancode::SDL_SCANCODE_AGAIN as i32,
    Undo               = SDL_Scancode::SDL_SCANCODE_UNDO as i32,
    Cut                = SDL_Scancode::SDL_SCANCODE_CUT as i32,
    Copy               = SDL_Scancode::SDL_SCANCODE_COPY as i32,
    Paste              = SDL_Scancode::SDL_SCANCODE_PASTE as i32,
    Find               = SDL_Scancode::SDL_SCANCODE_FIND as i32,
    Mute               = SDL_Scancode::SDL_SCANCODE_MUTE as i32,
    VolumeUp           = SDL_Scancode::SDL_SCANCODE_VOLUMEUP as i32,
    VolumeDown         = SDL_Scancode::SDL_SCANCODE_VOLUMEDOWN as i32,
    KpComma            = SDL_Scancode::SDL_SCANCODE_KP_COMMA as i32,
    KpEqualsAS400      = SDL_Scancode::SDL_SCANCODE_KP_EQUALSAS400 as i32,
    International1     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL1 as i32,
    International2     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL2 as i32,
    International3     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL3 as i32,
    International4     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL4 as i32,
    International5     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL5 as i32,
    International6     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL6 as i32,
    International7     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL7 as i32,
    International8     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL8 as i32,
    International9     = SDL_Scancode::SDL_SCANCODE_INTERNATIONAL9 as i32,
    Lang1              = SDL_Scancode::SDL_SCANCODE_LANG1 as i32,
    Lang2              = SDL_Scancode::SDL_SCANCODE_LANG2 as i32,
    Lang3              = SDL_Scancode::SDL_SCANCODE_LANG3 as i32,
    Lang4              = SDL_Scancode::SDL_SCANCODE_LANG4 as i32,
    Lang5              = SDL_Scancode::SDL_SCANCODE_LANG5 as i32,
    Lang6              = SDL_Scancode::SDL_SCANCODE_LANG6 as i32,
    Lang7              = SDL_Scancode::SDL_SCANCODE_LANG7 as i32,
    Lang8              = SDL_Scancode::SDL_SCANCODE_LANG8 as i32,
    Lang9              = SDL_Scancode::SDL_SCANCODE_LANG9 as i32,
    AltErase           = SDL_Scancode::SDL_SCANCODE_ALTERASE as i32,
    SysReq             = SDL_Scancode::SDL_SCANCODE_SYSREQ as i32,
    Cancel             = SDL_Scancode::SDL_SCANCODE_CANCEL as i32,
    Clear              = SDL_Scancode::SDL_SCANCODE_CLEAR as i32,
    Prior              = SDL_Scancode::SDL_SCANCODE_PRIOR as i32,
    Return2            = SDL_Scancode::SDL_SCANCODE_RETURN2 as i32,
    Separator          = SDL_Scancode::SDL_SCANCODE_SEPARATOR as i32,
    Out                = SDL_Scancode::SDL_SCANCODE_OUT as i32,
    Oper               = SDL_Scancode::SDL_SCANCODE_OPER as i32,
    ClearAgain         = SDL_Scancode::SDL_SCANCODE_CLEARAGAIN as i32,
    CrSel              = SDL_Scancode::SDL_SCANCODE_CRSEL as i32,
    ExSel              = SDL_Scancode::SDL_SCANCODE_EXSEL as i32,
    Kp00               = SDL_Scancode::SDL_SCANCODE_KP_00 as i32,
    Kp000              = SDL_Scancode::SDL_SCANCODE_KP_000 as i32,
    ThousandsSeparator = SDL_Scancode::SDL_SCANCODE_THOUSANDSSEPARATOR as i32,
    DecimalSeparator   = SDL_Scancode::SDL_SCANCODE_DECIMALSEPARATOR as i32,
    CurrencyUnit       = SDL_Scancode::SDL_SCANCODE_CURRENCYUNIT as i32,
    CurrencySubUnit    = SDL_Scancode::SDL_SCANCODE_CURRENCYSUBUNIT as i32,
    KpLeftParen        = SDL_Scancode::SDL_SCANCODE_KP_LEFTPAREN as i32,
    KpRightParen       = SDL_Scancode::SDL_SCANCODE_KP_RIGHTPAREN as i32,
    KpLeftBrace        = SDL_Scancode::SDL_SCANCODE_KP_LEFTBRACE as i32,
    KpRightBrace       = SDL_Scancode::SDL_SCANCODE_KP_RIGHTBRACE as i32,
    KpTab              = SDL_Scancode::SDL_SCANCODE_KP_TAB as i32,
    KpBackspace        = SDL_Scancode::SDL_SCANCODE_KP_BACKSPACE as i32,
    KpA                = SDL_Scancode::SDL_SCANCODE_KP_A as i32,
    KpB                = SDL_Scancode::SDL_SCANCODE_KP_B as i32,
    KpC                = SDL_Scancode::SDL_SCANCODE_KP_C as i32,
    KpD                = SDL_Scancode::SDL_SCANCODE_KP_D as i32,
    KpE                = SDL_Scancode::SDL_SCANCODE_KP_E as i32,
    KpF                = SDL_Scancode::SDL_SCANCODE_KP_F as i32,
    KpXor              = SDL_Scancode::SDL_SCANCODE_KP_XOR as i32,
    KpPower            = SDL_Scancode::SDL_SCANCODE_KP_POWER as i32,
    KpPercent          = SDL_Scancode::SDL_SCANCODE_KP_PERCENT as i32,
    KpLess             = SDL_Scancode::SDL_SCANCODE_KP_LESS as i32,
    KpGreater          = SDL_Scancode::SDL_SCANCODE_KP_GREATER as i32,
    KpAmpersand        = SDL_Scancode::SDL_SCANCODE_KP_AMPERSAND as i32,
    KpDblAmpersand     = SDL_Scancode::SDL_SCANCODE_KP_DBLAMPERSAND as i32,
    KpVerticalBar      = SDL_Scancode::SDL_SCANCODE_KP_VERTICALBAR as i32,
    KpDblVerticalBar   = SDL_Scancode::SDL_SCANCODE_KP_DBLVERTICALBAR as i32,
    KpColon            = SDL_Scancode::SDL_SCANCODE_KP_COLON as i32,
    KpHash             = SDL_Scancode::SDL_SCANCODE_KP_HASH as i32,
    KpSpace            = SDL_Scancode::SDL_SCANCODE_KP_SPACE as i32,
    KpAt               = SDL_Scancode::SDL_SCANCODE_KP_AT as i32,
    KpExclam           = SDL_Scancode::SDL_SCANCODE_KP_EXCLAM as i32,
    KpMemStore         = SDL_Scancode::SDL_SCANCODE_KP_MEMSTORE as i32,
    KpMemRecall        = SDL_Scancode::SDL_SCANCODE_KP_MEMRECALL as i32,
    KpMemClear         = SDL_Scancode::SDL_SCANCODE_KP_MEMCLEAR as i32,
    KpMemAdd           = SDL_Scancode::SDL_SCANCODE_KP_MEMADD as i32,
    KpMemSubtract      = SDL_Scancode::SDL_SCANCODE_KP_MEMSUBTRACT as i32,
    KpMemMultiply      = SDL_Scancode::SDL_SCANCODE_KP_MEMMULTIPLY as i32,
    KpMemDivide        = SDL_Scancode::SDL_SCANCODE_KP_MEMDIVIDE as i32,
    KpPlusMinus        = SDL_Scancode::SDL_SCANCODE_KP_PLUSMINUS as i32,
    KpClear            = SDL_Scancode::SDL_SCANCODE_KP_CLEAR as i32,
    KpClearEntry       = SDL_Scancode::SDL_SCANCODE_KP_CLEARENTRY as i32,
    KpBinary           = SDL_Scancode::SDL_SCANCODE_KP_BINARY as i32,
    KpOctal            = SDL_Scancode::SDL_SCANCODE_KP_OCTAL as i32,
    KpDecimal          = SDL_Scancode::SDL_SCANCODE_KP_DECIMAL as i32,
    KpHexadecimal      = SDL_Scancode::SDL_SCANCODE_KP_HEXADECIMAL as i32,
    LCtrl              = SDL_Scancode::SDL_SCANCODE_LCTRL as i32,
    LShift             = SDL_Scancode::SDL_SCANCODE_LSHIFT as i32,
    LAlt               = SDL_Scancode::SDL_SCANCODE_LALT as i32,
    LGui               = SDL_Scancode::SDL_SCANCODE_LGUI as i32,
    RCtrl              = SDL_Scancode::SDL_SCANCODE_RCTRL as i32,
    RShift             = SDL_Scancode::SDL_SCANCODE_RSHIFT as i32,
    RAlt               = SDL_Scancode::SDL_SCANCODE_RALT as i32,
    RGui               = SDL_Scancode::SDL_SCANCODE_RGUI as i32,
    Mode               = SDL_Scancode::SDL_SCANCODE_MODE as i32,
    AudioNext          = SDL_Scancode::SDL_SCANCODE_AUDIONEXT as i32,
    AudioPrev          = SDL_Scancode::SDL_SCANCODE_AUDIOPREV as i32,
    AudioStop          = SDL_Scancode::SDL_SCANCODE_AUDIOSTOP as i32,
    AudioPlay          = SDL_Scancode::SDL_SCANCODE_AUDIOPLAY as i32,
    AudioMute          = SDL_Scancode::SDL_SCANCODE_AUDIOMUTE as i32,
    MediaSelect        = SDL_Scancode::SDL_SCANCODE_MEDIASELECT as i32,
    Www                = SDL_Scancode::SDL_SCANCODE_WWW as i32,
    Mail               = SDL_Scancode::SDL_SCANCODE_MAIL as i32,
    Calculator         = SDL_Scancode::SDL_SCANCODE_CALCULATOR as i32,
    Computer           = SDL_Scancode::SDL_SCANCODE_COMPUTER as i32,
    AcSearch           = SDL_Scancode::SDL_SCANCODE_AC_SEARCH as i32,
    AcHome             = SDL_Scancode::SDL_SCANCODE_AC_HOME as i32,
    AcBack             = SDL_Scancode::SDL_SCANCODE_AC_BACK as i32,
    AcForward          = SDL_Scancode::SDL_SCANCODE_AC_FORWARD as i32,
    AcStop             = SDL_Scancode::SDL_SCANCODE_AC_STOP as i32,
    AcRefresh          = SDL_Scancode::SDL_SCANCODE_AC_REFRESH as i32,
    AcBookmarks        = SDL_Scancode::SDL_SCANCODE_AC_BOOKMARKS as i32,
    BrightnessDown     = SDL_Scancode::SDL_SCANCODE_BRIGHTNESSDOWN as i32,
    BrightnessUp       = SDL_Scancode::SDL_SCANCODE_BRIGHTNESSUP as i32,
    DisplaySwitch      = SDL_Scancode::SDL_SCANCODE_DISPLAYSWITCH as i32,
    KbdIllumToggle     = SDL_Scancode::SDL_SCANCODE_KBDILLUMTOGGLE as i32,
    KbdIllumDown       = SDL_Scancode::SDL_SCANCODE_KBDILLUMDOWN as i32,
    KbdIllumUp         = SDL_Scancode::SDL_SCANCODE_KBDILLUMUP as i32,
    Eject              = SDL_Scancode::SDL_SCANCODE_EJECT as i32,
    Sleep              = SDL_Scancode::SDL_SCANCODE_SLEEP as i32,
    App1               = SDL_Scancode::SDL_SCANCODE_APP1 as i32,
    App2               = SDL_Scancode::SDL_SCANCODE_APP2 as i32,
    Num                = SDL_Scancode::SDL_NUM_SCANCODES as i32,
}

impl Scancode {
    pub fn from_i32(n: i32) -> Option<Scancode> {
        use self::Scancode::*;
        let n = n as u32;

        Some( match unsafe { transmute::<u32, SDL_Scancode>(n) } {
            SDL_Scancode::SDL_SCANCODE_A                  => A,
            SDL_Scancode::SDL_SCANCODE_B                  => B,
            SDL_Scancode::SDL_SCANCODE_C                  => C,
            SDL_Scancode::SDL_SCANCODE_D                  => D,
            SDL_Scancode::SDL_SCANCODE_E                  => E,
            SDL_Scancode::SDL_SCANCODE_F                  => F,
            SDL_Scancode::SDL_SCANCODE_G                  => G,
            SDL_Scancode::SDL_SCANCODE_H                  => H,
            SDL_Scancode::SDL_SCANCODE_I                  => I,
            SDL_Scancode::SDL_SCANCODE_J                  => J,
            SDL_Scancode::SDL_SCANCODE_K                  => K,
            SDL_Scancode::SDL_SCANCODE_L                  => L,
            SDL_Scancode::SDL_SCANCODE_M                  => M,
            SDL_Scancode::SDL_SCANCODE_N                  => N,
            SDL_Scancode::SDL_SCANCODE_O                  => O,
            SDL_Scancode::SDL_SCANCODE_P                  => P,
            SDL_Scancode::SDL_SCANCODE_Q                  => Q,
            SDL_Scancode::SDL_SCANCODE_R                  => R,
            SDL_Scancode::SDL_SCANCODE_S                  => S,
            SDL_Scancode::SDL_SCANCODE_T                  => T,
            SDL_Scancode::SDL_SCANCODE_U                  => U,
            SDL_Scancode::SDL_SCANCODE_V                  => V,
            SDL_Scancode::SDL_SCANCODE_W                  => W,
            SDL_Scancode::SDL_SCANCODE_X                  => X,
            SDL_Scancode::SDL_SCANCODE_Y                  => Y,
            SDL_Scancode::SDL_SCANCODE_Z                  => Z,
            SDL_Scancode::SDL_SCANCODE_1                  => Num1,
            SDL_Scancode::SDL_SCANCODE_2                  => Num2,
            SDL_Scancode::SDL_SCANCODE_3                  => Num3,
            SDL_Scancode::SDL_SCANCODE_4                  => Num4,
            SDL_Scancode::SDL_SCANCODE_5                  => Num5,
            SDL_Scancode::SDL_SCANCODE_6                  => Num6,
            SDL_Scancode::SDL_SCANCODE_7                  => Num7,
            SDL_Scancode::SDL_SCANCODE_8                  => Num8,
            SDL_Scancode::SDL_SCANCODE_9                  => Num9,
            SDL_Scancode::SDL_SCANCODE_0                  => Num0,
            SDL_Scancode::SDL_SCANCODE_RETURN             => Return,
            SDL_Scancode::SDL_SCANCODE_ESCAPE             => Escape,
            SDL_Scancode::SDL_SCANCODE_BACKSPACE          => Backspace,
            SDL_Scancode::SDL_SCANCODE_TAB                => Tab,
            SDL_Scancode::SDL_SCANCODE_SPACE              => Space,
            SDL_Scancode::SDL_SCANCODE_MINUS              => Minus,
            SDL_Scancode::SDL_SCANCODE_EQUALS             => Equals,
            SDL_Scancode::SDL_SCANCODE_LEFTBRACKET        => LeftBracket,
            SDL_Scancode::SDL_SCANCODE_RIGHTBRACKET       => RightBracket,
            SDL_Scancode::SDL_SCANCODE_BACKSLASH          => Backslash,
            SDL_Scancode::SDL_SCANCODE_NONUSHASH          => NonUsHash,
            SDL_Scancode::SDL_SCANCODE_SEMICOLON          => Semicolon,
            SDL_Scancode::SDL_SCANCODE_APOSTROPHE         => Apostrophe,
            SDL_Scancode::SDL_SCANCODE_GRAVE              => Grave,
            SDL_Scancode::SDL_SCANCODE_COMMA              => Comma,
            SDL_Scancode::SDL_SCANCODE_PERIOD             => Period,
            SDL_Scancode::SDL_SCANCODE_SLASH              => Slash,
            SDL_Scancode::SDL_SCANCODE_CAPSLOCK           => CapsLock,
            SDL_Scancode::SDL_SCANCODE_F1                 => F1,
            SDL_Scancode::SDL_SCANCODE_F2                 => F2,
            SDL_Scancode::SDL_SCANCODE_F3                 => F3,
            SDL_Scancode::SDL_SCANCODE_F4                 => F4,
            SDL_Scancode::SDL_SCANCODE_F5                 => F5,
            SDL_Scancode::SDL_SCANCODE_F6                 => F6,
            SDL_Scancode::SDL_SCANCODE_F7                 => F7,
            SDL_Scancode::SDL_SCANCODE_F8                 => F8,
            SDL_Scancode::SDL_SCANCODE_F9                 => F9,
            SDL_Scancode::SDL_SCANCODE_F10                => F10,
            SDL_Scancode::SDL_SCANCODE_F11                => F11,
            SDL_Scancode::SDL_SCANCODE_F12                => F12,
            SDL_Scancode::SDL_SCANCODE_PRINTSCREEN        => PrintScreen,
            SDL_Scancode::SDL_SCANCODE_SCROLLLOCK         => ScrollLock,
            SDL_Scancode::SDL_SCANCODE_PAUSE              => Pause,
            SDL_Scancode::SDL_SCANCODE_INSERT             => Insert,
            SDL_Scancode::SDL_SCANCODE_HOME               => Home,
            SDL_Scancode::SDL_SCANCODE_PAGEUP             => PageUp,
            SDL_Scancode::SDL_SCANCODE_DELETE             => Delete,
            SDL_Scancode::SDL_SCANCODE_END                => End,
            SDL_Scancode::SDL_SCANCODE_PAGEDOWN           => PageDown,
            SDL_Scancode::SDL_SCANCODE_RIGHT              => Right,
            SDL_Scancode::SDL_SCANCODE_LEFT               => Left,
            SDL_Scancode::SDL_SCANCODE_DOWN               => Down,
            SDL_Scancode::SDL_SCANCODE_UP                 => Up,
            SDL_Scancode::SDL_SCANCODE_NUMLOCKCLEAR       => NumLockClear,
            SDL_Scancode::SDL_SCANCODE_KP_DIVIDE          => KpDivide,
            SDL_Scancode::SDL_SCANCODE_KP_MULTIPLY        => KpMultiply,
            SDL_Scancode::SDL_SCANCODE_KP_MINUS           => KpMinus,
            SDL_Scancode::SDL_SCANCODE_KP_PLUS            => KpPlus,
            SDL_Scancode::SDL_SCANCODE_KP_ENTER           => KpEnter,
            SDL_Scancode::SDL_SCANCODE_KP_1               => Kp1,
            SDL_Scancode::SDL_SCANCODE_KP_2               => Kp2,
            SDL_Scancode::SDL_SCANCODE_KP_3               => Kp3,
            SDL_Scancode::SDL_SCANCODE_KP_4               => Kp4,
            SDL_Scancode::SDL_SCANCODE_KP_5               => Kp5,
            SDL_Scancode::SDL_SCANCODE_KP_6               => Kp6,
            SDL_Scancode::SDL_SCANCODE_KP_7               => Kp7,
            SDL_Scancode::SDL_SCANCODE_KP_8               => Kp8,
            SDL_Scancode::SDL_SCANCODE_KP_9               => Kp9,
            SDL_Scancode::SDL_SCANCODE_KP_0               => Kp0,
            SDL_Scancode::SDL_SCANCODE_KP_PERIOD          => KpPeriod,
            SDL_Scancode::SDL_SCANCODE_NONUSBACKSLASH     => NonUsBackslash,
            SDL_Scancode::SDL_SCANCODE_APPLICATION        => Application,
            SDL_Scancode::SDL_SCANCODE_POWER              => Power,
            SDL_Scancode::SDL_SCANCODE_KP_EQUALS          => KpEquals,
            SDL_Scancode::SDL_SCANCODE_F13                => F13,
            SDL_Scancode::SDL_SCANCODE_F14                => F14,
            SDL_Scancode::SDL_SCANCODE_F15                => F15,
            SDL_Scancode::SDL_SCANCODE_F16                => F16,
            SDL_Scancode::SDL_SCANCODE_F17                => F17,
            SDL_Scancode::SDL_SCANCODE_F18                => F18,
            SDL_Scancode::SDL_SCANCODE_F19                => F19,
            SDL_Scancode::SDL_SCANCODE_F20                => F20,
            SDL_Scancode::SDL_SCANCODE_F21                => F21,
            SDL_Scancode::SDL_SCANCODE_F22                => F22,
            SDL_Scancode::SDL_SCANCODE_F23                => F23,
            SDL_Scancode::SDL_SCANCODE_F24                => F24,
            SDL_Scancode::SDL_SCANCODE_EXECUTE            => Execute,
            SDL_Scancode::SDL_SCANCODE_HELP               => Help,
            SDL_Scancode::SDL_SCANCODE_MENU               => Menu,
            SDL_Scancode::SDL_SCANCODE_SELECT             => Select,
            SDL_Scancode::SDL_SCANCODE_STOP               => Stop,
            SDL_Scancode::SDL_SCANCODE_AGAIN              => Again,
            SDL_Scancode::SDL_SCANCODE_UNDO               => Undo,
            SDL_Scancode::SDL_SCANCODE_CUT                => Cut,
            SDL_Scancode::SDL_SCANCODE_COPY               => Copy,
            SDL_Scancode::SDL_SCANCODE_PASTE              => Paste,
            SDL_Scancode::SDL_SCANCODE_FIND               => Find,
            SDL_Scancode::SDL_SCANCODE_MUTE               => Mute,
            SDL_Scancode::SDL_SCANCODE_VOLUMEUP           => VolumeUp,
            SDL_Scancode::SDL_SCANCODE_VOLUMEDOWN         => VolumeDown,
            SDL_Scancode::SDL_SCANCODE_KP_COMMA           => KpComma,
            SDL_Scancode::SDL_SCANCODE_KP_EQUALSAS400     => KpEqualsAS400,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL1     => International1,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL2     => International2,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL3     => International3,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL4     => International4,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL5     => International5,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL6     => International6,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL7     => International7,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL8     => International8,
            SDL_Scancode::SDL_SCANCODE_INTERNATIONAL9     => International9,
            SDL_Scancode::SDL_SCANCODE_LANG1              => Lang1,
            SDL_Scancode::SDL_SCANCODE_LANG2              => Lang2,
            SDL_Scancode::SDL_SCANCODE_LANG3              => Lang3,
            SDL_Scancode::SDL_SCANCODE_LANG4              => Lang4,
            SDL_Scancode::SDL_SCANCODE_LANG5              => Lang5,
            SDL_Scancode::SDL_SCANCODE_LANG6              => Lang6,
            SDL_Scancode::SDL_SCANCODE_LANG7              => Lang7,
            SDL_Scancode::SDL_SCANCODE_LANG8              => Lang8,
            SDL_Scancode::SDL_SCANCODE_LANG9              => Lang9,
            SDL_Scancode::SDL_SCANCODE_ALTERASE           => AltErase,
            SDL_Scancode::SDL_SCANCODE_SYSREQ             => SysReq,
            SDL_Scancode::SDL_SCANCODE_CANCEL             => Cancel,
            SDL_Scancode::SDL_SCANCODE_CLEAR              => Clear,
            SDL_Scancode::SDL_SCANCODE_PRIOR              => Prior,
            SDL_Scancode::SDL_SCANCODE_RETURN2            => Return2,
            SDL_Scancode::SDL_SCANCODE_SEPARATOR          => Separator,
            SDL_Scancode::SDL_SCANCODE_OUT                => Out,
            SDL_Scancode::SDL_SCANCODE_OPER               => Oper,
            SDL_Scancode::SDL_SCANCODE_CLEARAGAIN         => ClearAgain,
            SDL_Scancode::SDL_SCANCODE_CRSEL              => CrSel,
            SDL_Scancode::SDL_SCANCODE_EXSEL              => ExSel,
            SDL_Scancode::SDL_SCANCODE_KP_00              => Kp00,
            SDL_Scancode::SDL_SCANCODE_KP_000             => Kp000,
            SDL_Scancode::SDL_SCANCODE_THOUSANDSSEPARATOR => ThousandsSeparator,
            SDL_Scancode::SDL_SCANCODE_DECIMALSEPARATOR   => DecimalSeparator,
            SDL_Scancode::SDL_SCANCODE_CURRENCYUNIT       => CurrencyUnit,
            SDL_Scancode::SDL_SCANCODE_CURRENCYSUBUNIT    => CurrencySubUnit,
            SDL_Scancode::SDL_SCANCODE_KP_LEFTPAREN       => KpLeftParen,
            SDL_Scancode::SDL_SCANCODE_KP_RIGHTPAREN      => KpRightParen,
            SDL_Scancode::SDL_SCANCODE_KP_LEFTBRACE       => KpLeftBrace,
            SDL_Scancode::SDL_SCANCODE_KP_RIGHTBRACE      => KpRightBrace,
            SDL_Scancode::SDL_SCANCODE_KP_TAB             => KpTab,
            SDL_Scancode::SDL_SCANCODE_KP_BACKSPACE       => KpBackspace,
            SDL_Scancode::SDL_SCANCODE_KP_A               => KpA,
            SDL_Scancode::SDL_SCANCODE_KP_B               => KpB,
            SDL_Scancode::SDL_SCANCODE_KP_C               => KpC,
            SDL_Scancode::SDL_SCANCODE_KP_D               => KpD,
            SDL_Scancode::SDL_SCANCODE_KP_E               => KpE,
            SDL_Scancode::SDL_SCANCODE_KP_F               => KpF,
            SDL_Scancode::SDL_SCANCODE_KP_XOR             => KpXor,
            SDL_Scancode::SDL_SCANCODE_KP_POWER           => KpPower,
            SDL_Scancode::SDL_SCANCODE_KP_PERCENT         => KpPercent,
            SDL_Scancode::SDL_SCANCODE_KP_LESS            => KpLess,
            SDL_Scancode::SDL_SCANCODE_KP_GREATER         => KpGreater,
            SDL_Scancode::SDL_SCANCODE_KP_AMPERSAND       => KpAmpersand,
            SDL_Scancode::SDL_SCANCODE_KP_DBLAMPERSAND    => KpDblAmpersand,
            SDL_Scancode::SDL_SCANCODE_KP_VERTICALBAR     => KpVerticalBar,
            SDL_Scancode::SDL_SCANCODE_KP_DBLVERTICALBAR  => KpDblVerticalBar,
            SDL_Scancode::SDL_SCANCODE_KP_COLON           => KpColon,
            SDL_Scancode::SDL_SCANCODE_KP_HASH            => KpHash,
            SDL_Scancode::SDL_SCANCODE_KP_SPACE           => KpSpace,
            SDL_Scancode::SDL_SCANCODE_KP_AT              => KpAt,
            SDL_Scancode::SDL_SCANCODE_KP_EXCLAM          => KpExclam,
            SDL_Scancode::SDL_SCANCODE_KP_MEMSTORE        => KpMemStore,
            SDL_Scancode::SDL_SCANCODE_KP_MEMRECALL       => KpMemRecall,
            SDL_Scancode::SDL_SCANCODE_KP_MEMCLEAR        => KpMemClear,
            SDL_Scancode::SDL_SCANCODE_KP_MEMADD          => KpMemAdd,
            SDL_Scancode::SDL_SCANCODE_KP_MEMSUBTRACT     => KpMemSubtract,
            SDL_Scancode::SDL_SCANCODE_KP_MEMMULTIPLY     => KpMemMultiply,
            SDL_Scancode::SDL_SCANCODE_KP_MEMDIVIDE       => KpMemDivide,
            SDL_Scancode::SDL_SCANCODE_KP_PLUSMINUS       => KpPlusMinus,
            SDL_Scancode::SDL_SCANCODE_KP_CLEAR           => KpClear,
            SDL_Scancode::SDL_SCANCODE_KP_CLEARENTRY      => KpClearEntry,
            SDL_Scancode::SDL_SCANCODE_KP_BINARY          => KpBinary,
            SDL_Scancode::SDL_SCANCODE_KP_OCTAL           => KpOctal,
            SDL_Scancode::SDL_SCANCODE_KP_DECIMAL         => KpDecimal,
            SDL_Scancode::SDL_SCANCODE_KP_HEXADECIMAL     => KpHexadecimal,
            SDL_Scancode::SDL_SCANCODE_LCTRL              => LCtrl,
            SDL_Scancode::SDL_SCANCODE_LSHIFT             => LShift,
            SDL_Scancode::SDL_SCANCODE_LALT               => LAlt,
            SDL_Scancode::SDL_SCANCODE_LGUI               => LGui,
            SDL_Scancode::SDL_SCANCODE_RCTRL              => RCtrl,
            SDL_Scancode::SDL_SCANCODE_RSHIFT             => RShift,
            SDL_Scancode::SDL_SCANCODE_RALT               => RAlt,
            SDL_Scancode::SDL_SCANCODE_RGUI               => RGui,
            SDL_Scancode::SDL_SCANCODE_MODE               => Mode,
            SDL_Scancode::SDL_SCANCODE_AUDIONEXT          => AudioNext,
            SDL_Scancode::SDL_SCANCODE_AUDIOPREV          => AudioPrev,
            SDL_Scancode::SDL_SCANCODE_AUDIOSTOP          => AudioStop,
            SDL_Scancode::SDL_SCANCODE_AUDIOPLAY          => AudioPlay,
            SDL_Scancode::SDL_SCANCODE_AUDIOMUTE          => AudioMute,
            SDL_Scancode::SDL_SCANCODE_MEDIASELECT        => MediaSelect,
            SDL_Scancode::SDL_SCANCODE_WWW                => Www,
            SDL_Scancode::SDL_SCANCODE_MAIL               => Mail,
            SDL_Scancode::SDL_SCANCODE_CALCULATOR         => Calculator,
            SDL_Scancode::SDL_SCANCODE_COMPUTER           => Computer,
            SDL_Scancode::SDL_SCANCODE_AC_SEARCH          => AcSearch,
            SDL_Scancode::SDL_SCANCODE_AC_HOME            => AcHome,
            SDL_Scancode::SDL_SCANCODE_AC_BACK            => AcBack,
            SDL_Scancode::SDL_SCANCODE_AC_FORWARD         => AcForward,
            SDL_Scancode::SDL_SCANCODE_AC_STOP            => AcStop,
            SDL_Scancode::SDL_SCANCODE_AC_REFRESH         => AcRefresh,
            SDL_Scancode::SDL_SCANCODE_AC_BOOKMARKS       => AcBookmarks,
            SDL_Scancode::SDL_SCANCODE_BRIGHTNESSDOWN     => BrightnessDown,
            SDL_Scancode::SDL_SCANCODE_BRIGHTNESSUP       => BrightnessUp,
            SDL_Scancode::SDL_SCANCODE_DISPLAYSWITCH      => DisplaySwitch,
            SDL_Scancode::SDL_SCANCODE_KBDILLUMTOGGLE     => KbdIllumToggle,
            SDL_Scancode::SDL_SCANCODE_KBDILLUMDOWN       => KbdIllumDown,
            SDL_Scancode::SDL_SCANCODE_KBDILLUMUP         => KbdIllumUp,
            SDL_Scancode::SDL_SCANCODE_EJECT              => Eject,
            SDL_Scancode::SDL_SCANCODE_SLEEP              => Sleep,
            SDL_Scancode::SDL_SCANCODE_APP1               => App1,
            SDL_Scancode::SDL_SCANCODE_APP2               => App2,
            SDL_Scancode::SDL_NUM_SCANCODES               => Num,
            _                                                  => return None,
        })
    }
}

use std::fmt;

impl fmt::Display for Scancode {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.name())
    }
}

use crate::keyboard::Keycode;

impl Scancode {
    /// Gets the scancode from a virtual key. Returns None if there is no corresponding scancode.
    pub fn from_keycode(keycode: Keycode) -> Option<Scancode> {
        unsafe {
            match sys::SDL_GetScancodeFromKey(keycode as i32) {
                SDL_Scancode::SDL_SCANCODE_UNKNOWN => None,
                scancode_id => Scancode::from_i32(scancode_id as i32)
            }
        }
    }

    pub fn from_name(name: &str) -> Option<Scancode> {
        unsafe {
            match CString::new(name) {
                Ok(name) => match sys::SDL_GetScancodeFromName(name.as_ptr() as *const c_char) {
                    SDL_Scancode::SDL_SCANCODE_UNKNOWN => None,
                    scancode_id => Some(Scancode::from_i32(scancode_id as i32).unwrap())
                },
                // string contains a nul byte - it won't match anything.
                Err(_) => None
            }
        }
    }

    pub fn name(self) -> &'static str {
        // The name string pointer lives in static, read-only memory.
        // Knowing this, we can always return a string slice.
        unsafe {
            let buf = sys::SDL_GetScancodeName(transmute::<u32, SDL_Scancode>(self as u32));
            ::std::str::from_utf8(CStr::from_ptr(buf as *const _).to_bytes()).unwrap()
        }
    }
}
